﻿using System;
using System.Text;
using System.Diagnostics;
using System.Threading;
using System.Runtime.InteropServices;

namespace Framework.Common
{
    public static class PerformanceHelper
    {
        #region output Method set
        private static Action<object> _outputAction = LogHelper.Debug;
        /// <summary>
        /// out put method
        /// </summary>
        public static Action<object> OutputAction
        {
            set
            {
                if (value != null)
                {
                    _outputAction = value;
                }
            }
        }
        #endregion

        #region static constructor
        static PerformanceHelper()
        {
            Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
            Thread.CurrentThread.Priority = ThreadPriority.Highest;
            Time(() => { }, 1, string.Empty, false);
        }
        #endregion

        /// <summary>
        /// 
        /// </summary>
        /// <param name="action"></param>
        /// <param name="iteration"></param>
        /// <param name="tagName"> </param>
        public static void Time(Action action, int iteration, string tagName)
        {
            Time(action, iteration, tagName, true);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="action"></param>
        /// <param name="iteration"></param>
        /// <param name="showLog"></param>
        public static void Time(Action action, string tagName)
        {
            Time(action, 1, tagName, true);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="action"></param>
        /// <param name="iteration"></param>
        /// <param name="showLog"></param>
        public static void Time(Action action, int iteration)
        {
            Time(action, iteration, string.Empty, true);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="action"></param>
        /// <param name="showLog"></param>
        public static void Time(Action action)
        {
            Time(action, 1, string.Empty, true);
        }

        #region implement
        /// <summary>
        /// 
        /// </summary>
        /// <param name="action"></param>
        /// <param name="iteration"></param>
        /// <param name="tagName"></param>
        /// <param name="output"></param>
        /// <param name="showLog"></param>
        static void Time(Action action, int iteration, string tagName, bool showLog)
        {
            StringBuilder sb = new StringBuilder();
            // 1.
            sb.AppendLine(string.Empty);
            sb.AppendLine(tagName + "  ========Start==========");

            // 2.
            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
            int[] gcCounts = new int[GC.MaxGeneration + 1];
            for (int i = 0; i <= GC.MaxGeneration; i++)
            {
                gcCounts[i] = GC.CollectionCount(i);
            }

            // 3.
            Stopwatch watch = new Stopwatch();
            watch.Start();
            ulong cycleCount = GetCycleCount();
            for (int i = 0; i < iteration; i++)
            {
                action();
            }
            ulong cpuCycles = GetCycleCount() - cycleCount;
            watch.Stop();

            // 4.
            sb.AppendLine("\tIteration:\t" + iteration);
            sb.AppendLine("\tTime Elapsed Per Times:\t" + ((double)watch.ElapsedMilliseconds / (double)iteration) + "ms");
            sb.AppendLine("\tTime Elapsed:\t" + watch.ElapsedMilliseconds.ToString("N0") + "ms");
            sb.AppendLine("\tCPU Cycles:\t" + cpuCycles.ToString("N0"));

            // 5.
            for (int i = 0; i <= GC.MaxGeneration; i++)
            {
                int count = GC.CollectionCount(i) - gcCounts[i];
                sb.AppendLine("\tGen " + i + ": \t\t" + count);
            }

            //6
            sb.AppendLine(tagName + "  ========End===========");
            sb.AppendLine(string.Empty);

            if (showLog && _outputAction != null)
            {
                _outputAction(sb);
            }
        }

        private static ulong GetCycleCount()
        {
            ulong cycleCount = 0;
            QueryThreadCycleTime(GetCurrentThread(), ref cycleCount);
            return cycleCount;
        }

        [DllImport("kernel32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool QueryThreadCycleTime(IntPtr threadHandle, ref ulong cycleTime);

        [DllImport("kernel32.dll")]
        static extern IntPtr GetCurrentThread();
        #endregion
    }
}
